package com.awesome.db;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.text.TextUtils;
import android.util.Log;

import com.awesome.db.annotation.Column;
import com.awesome.db.annotation.PrimaryKey;
import com.awesome.db.annotation.Table;
import com.awesome.db.bean.ColumnInfoBean;
import com.awesome.db.bean.PrimaryKeyColumnInfoBean;
import com.awesome.db.bean.TableInfoBean;
import com.awesome.db.util.SqlUtil;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;


/**
 * Description:
 */
public abstract class DatabaseHelper extends SQLiteOpenHelper {
    private static final String TAG = DatabaseHelper.class.getSimpleName();
    final Context mContext;
    boolean mShowSql = false;
    /**
     * Map<Class, String> 中 Key 为表映射类 class,Value 为表信息
     */
    final Map<Class<?>, TableInfoBean> mTableInfoBeanMap = new HashMap<>();
    /**
     * 预置 sql 的集合
     */
    final Map<Class<?>, Map<String, String>> mPresetSqlMap = new HashMap<>();

    /**
     * @param context    上下文
     * @param name       数据库名称
     * @param version    数据库版本
     * @param classArray 需要存储的类的 class 集合，会根据该集合去创建对应表
     */
    public DatabaseHelper(Context context, String name, int version, Class<?>... classArray) {
        super(context, name, null, version);
        mContext = context;
        mShowSql = context.getResources().getBoolean(R.bool.showSql);
        for (Class<?> clazz : classArray) {
            mTableInfoBeanMap.put(clazz, parseTableInfo(clazz));
        }
        for (Map.Entry<Class<?>, TableInfoBean> entry : mTableInfoBeanMap.entrySet()) {
            HashMap<String, String> map = new HashMap<>();
            map.put("${insert}", SqlUtil.getInsertSql(entry.getValue()));
            map.put("${deleteById}", SqlUtil.getDeleteByIdSql(entry.getValue()));
            map.put("${updateById}", SqlUtil.getUpdateByIdSql(entry.getValue()));
            map.put("${selectById}", SqlUtil.getSelectByIdSql(entry.getValue()));
            map.put("${selectList}", SqlUtil.getSelectListSql(entry.getValue()));
            map.put("${existsById}", SqlUtil.getExistsByIdSql(entry.getValue()));
            map.put("${createTable}", SqlUtil.getCreateTableSql(entry.getValue()));
            map.put("${dropTable}", SqlUtil.getDropTableSql(entry.getValue()));
            mPresetSqlMap.put(entry.getKey(), map);
        }
    }

    /**
     * 初始化表,第一次创建表时调用
     *
     * @param db 数据库的操作类的对象
     */
    @Override
    public void onCreate(SQLiteDatabase db) {
        for (TableInfoBean tableInfoBean : mTableInfoBeanMap.values()) {
            String sql = SqlUtil.getCreateTableSql(tableInfoBean);
            if (mShowSql) {
                Log.i(TAG, sql);
            }
            db.execSQL(sql);
        }
        create(db);
    }

    /**
     * 更新数据库,如果数据库的版本号发生变化,执行此方法
     *
     * @param db         数据库的操作类的对象
     * @param oldVersion 旧版本
     * @param newVersion 新版本
     */
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        upgrade(db, oldVersion, newVersion);
    }

    public abstract void create(SQLiteDatabase db);

    public abstract void upgrade(SQLiteDatabase db, int oldVersion, int newVersion);

    /**
     * Author: JFangZ
     * Email:janezhang1901@163.com
     * Date:2020/3/19 12:57
     * Description:解析需要持久化的实体类,先解析一遍各属性,放到集合中,在创建 SQL 的时候就不用再次反射了
     *
     * @param clazz 需要持久化的实体类
     */
    private TableInfoBean parseTableInfo(Class<?> clazz) {
        TableInfoBean tableInfoBean = new TableInfoBean();
        tableInfoBean.setTableName(getTableName(clazz, clazz.getAnnotation(Table.class)));
        for (Field field : clazz.getDeclaredFields()) {
            Column column = field.getAnnotation(Column.class);
            if (column == null) {
                continue;
            }
            PrimaryKey primaryKey = field.getAnnotation(PrimaryKey.class);
            if (primaryKey == null) {
                // 非主键
                tableInfoBean.getColumnInfoBeanList().add(new ColumnInfoBean(getColumnName(field, column)
                        , field.getName()
                        , field.getType()
                        , column.unique()
                        , column.unionUnique()
                        , column.notNull()));
            } else {
                // 主键
                tableInfoBean.getPrimaryKeyColumnInfoBeanList().add(new PrimaryKeyColumnInfoBean(getColumnName(field, column)
                        , field.getName()
                        , field.getType()
                        , primaryKey.autoIncrement()
                        , primaryKey.useGeneratedKeys()));
            }
        }
        if (tableInfoBean.getPrimaryKeyColumnInfoBeanList().size() == 0) {
            throw new RuntimeException("class:" + clazz.getName() + " must assign primary key");
        }
        return tableInfoBean;
    }

    /**
     * Author: JFangZ
     * Email:janezhang1901@163.com
     * Date:2020/3/19 12:55
     * Description:获取表名
     *
     * @param clazz 表对应的实体类
     * @param table 实体类上的 Table 注解
     */
    private String getTableName(Class<?> clazz, Table table) {
        String tableName;
        if (table == null) {
            // 如果没有使用 Table 注解修饰需要存储的类则抛出异常
            throw new IllegalArgumentException("类 " + clazz.getName() + " 没有使用 Table 注解修饰");
        }
        tableName = table.name();
        // 如果没有指定 Table 的 name 属性,则使用类名作为表名
        if ("".equals(tableName.trim())) {
            tableName = clazz.getSimpleName();
        }
        return tableName;
    }

    /**
     * Author: JFangZ
     * Email:janezhang1901@163.com
     * Date:2020/3/19 12:55
     * Description:获取列名
     *
     * @param field  列对应的属性
     * @param column 属性上的 Column 注解
     */
    String getColumnName(Field field, Column column) {
        String columnName = column.name();
        // 如果没有指定 Column 的 name 属性,则使用属性名作为列名
        if (TextUtils.isEmpty(columnName.trim())) {
            columnName = field.getName();
        }
        return columnName;
    }
}
